﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Threading.Tasks;
using DynamicData;
using MilkTeaMachine.OfflineClient.EntityFrameworkCore;
using MilkTeaMachine.OfflineClient.EntityFrameworkCore.Models;
using MilkTeaMachine.OfflineClient.Models;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;

namespace MilkTeaMachine.OfflineClient.ViewModels;

public class IngredientInputViewModel : ViewModelBase, IRoutableViewModel
{
    internal const int LeftCount = 20;
    private const int RightCount = 20;
    private readonly ApplicationDbContext _db;

    public IngredientInputViewModel(IScreen hostScreen)
    {
        HostScreen = hostScreen;
        _db = Locator.Current.GetService<ApplicationDbContext>()!;
        IngredientObservableCollection = GetIngredientAddressVoList();

        IngredientListLeft =
            new ObservableCollection<IngredientAddressVo>(IngredientObservableCollection.Take(LeftCount));
        IngredientListRight =
            new ObservableCollection<IngredientAddressVo>(IngredientObservableCollection.Skip(SkipCount + LeftCount)
                .Take(LeftCount));
        PrePageCmd = ReactiveCommand.CreateFromTask(PrePage,
            this.WhenAnyValue(vm => vm.SkipCount, skipCount => skipCount > 0));
        NextPageCmd = ReactiveCommand.CreateFromTask(NextPage,
            this.WhenAny(vm => vm.SkipCount,
                vm => vm.IngredientObservableCollection.Count,
                (skipCount, total) => skipCount.Value + LeftCount + RightCount < total.Value));
        SaveAndQuitCmd = ReactiveCommand.CreateFromTask(SaveAndQuitAsync);
        GoBackCmd = ReactiveCommand.CreateFromObservable(() =>
            HostScreen.Router.NavigateBack.Execute());
    }

    public IngredientInputViewModel(): this(default!)
    {
        
    }

    internal List<Ingredient> Ingredients;
    internal readonly List<Ingredient> UpdateIngredients = new(8);
    internal readonly List<Ingredient> AddIngredients = new(8);

    private ObservableCollection<IngredientAddressVo> GetIngredientAddressVoList()
    {
        Ingredients = _db.Ingredients.ToList();
        var ingredientAddressList = Ingredients.Select((ingredient, index) => new IngredientAddressVo
        {
            IngredientName = ingredient.IngredientName,
            DeviceAddress = ingredient.DeviceAddress,
            Description = ingredient.Description,
            Id = ingredient.Id,
            Manufacturer = ingredient.Manufacturer,
            Sequence = index + 1
        }).ToList();
        int currentIndex;
        if (ingredientAddressList.Count != 0)
        {
            currentIndex = ingredientAddressList.Last().Sequence + 1;
        }
        else
        {
            currentIndex = 1;
        }

        ingredientAddressList.Add(new IngredientAddressVo
        {
            IngredientName = "",
            DeviceAddress = -1,
            Sequence = currentIndex,
            Id = -1
        });
        return new ObservableCollection<IngredientAddressVo>(ingredientAddressList);
    }

    private async Task SaveAndQuitAsync()
    {
        if (UpdateIngredients.Count != 0)
        {
            _db.Ingredients.UpdateRange(UpdateIngredients);
            await _db.SaveChangesAsync();
        }

        if (AddIngredients.Count != 0)
        {
            await _db.Ingredients.AddRangeAsync(AddIngredients);
            await _db.SaveChangesAsync();
        }

        HostScreen.Router.NavigateAndReset.Execute(new MainPageViewModel(HostScreen));
    }


    private Task PrePage()
    {
        SkipCount -= LeftCount + RightCount;
        IngredientListLeft.Clear();
        IngredientListRight.Clear();
        IngredientListLeft.AddRange(IngredientObservableCollection.Skip(SkipCount).Take(LeftCount));
        IngredientListRight.AddRange(IngredientObservableCollection.Skip(SkipCount + LeftCount).Take(LeftCount));
        return Task.CompletedTask;
    }

    private Task NextPage()
    {
        SkipCount += LeftCount + RightCount;
        IngredientListLeft.Clear();
        IngredientListRight.Clear();
        IngredientListLeft.AddRange(IngredientObservableCollection.Skip(SkipCount).Take(LeftCount));
        IngredientListRight.AddRange(IngredientObservableCollection.Skip(SkipCount + LeftCount).Take(LeftCount));
        return Task.CompletedTask;
    }

    public string? UrlPathSegment { get; } = nameof(IngredientInputViewModel);
    public IScreen HostScreen { get; }
    [Reactive] internal int SkipCount { get; set; }
    internal ObservableCollection<IngredientAddressVo> IngredientObservableCollection { get; set; }
    public ObservableCollection<IngredientAddressVo> IngredientListLeft { get; }
    public ObservableCollection<IngredientAddressVo> IngredientListRight { get; }
    public ReactiveCommand<Unit, Unit> PrePageCmd { get; }
    public ReactiveCommand<Unit, Unit> NextPageCmd { get; }
    public ReactiveCommand<Unit, Unit> SaveAndQuitCmd { get; }
    public ReactiveCommand<Unit, IRoutableViewModel> GoBackCmd { get; }
}